﻿#region License
// (c) Intergen.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
#endregion

using System;
using MediaWiki.Office.Model.Utilities;

namespace MediaWiki.Office.Model
{
  public struct Unit : IEquatable<Unit>
  {
    private readonly double _value;
    private readonly UnitType _type;

    public double Value
    {
      get { return _value; }
    } 

    public UnitType Type
    {
      get { return _type; }
    } 

    public Unit(double value, UnitType type)
    {
      if (type == UnitType.Auto && value != 0)
        throw new ArgumentException("Value must be zero for auto unit type.");

      _value = value;
      _type = type;
    }
    
    public double ToPixels()
    {
      switch (Type)
      {
        case UnitType.Pixel:
          return Value;
        case UnitType.Twip:
        case UnitType.EighthOfAPoint:
        case UnitType.HalfOfAPoint:
        case UnitType.Point:
        case UnitType.Emu:
          return MeasurementUtils.ToPixels(ToPoints());
        default:
          throw new ArgumentOutOfRangeException("Type", string.Format("Can not convert Unit of type {0} to pixels.", Type));
      }
    }

    public double ToPoints()
    {
      switch (Type)
      {
        case UnitType.Pixel:
          return MeasurementUtils.ToPoints(Value);
        case UnitType.Emu:
          return Value / 12700;
        case UnitType.Twip:
          return Value / 20;
        case UnitType.EighthOfAPoint:
          return Value / 8;
        case UnitType.HalfOfAPoint:
          return Value / 2;
        case UnitType.Point:
          return Value;
        default:
          throw new ArgumentOutOfRangeException("Type", string.Format("Can not convert Unit of type {0} to points.", Type));
      }
    }

    public static Unit Auto()
    {
      return new Unit(0, UnitType.Auto);
    }

    public static Unit? Percent(double? percentage)
    {
      if (percentage == null)
        return null;

      return Percent(percentage.Value);
    }

    public static Unit Percent(double percentage)
    {
      return new Unit(percentage, UnitType.Percent);
    }

    public static Unit? Point(double? points)
    {
      if (points == null)
        return null;

      return Point(points.Value);
    }

    public static Unit Point(double points)
    {
      return new Unit(points, UnitType.Point);
    }

    public static Unit? EighthOfAPoint(double? eighthsOfAPoint)
    {
      if (eighthsOfAPoint == null)
        return null;

      return EighthOfAPoint(eighthsOfAPoint.Value);
    }

    public static Unit EighthOfAPoint(double eighthsOfAPoint)
    {
      return new Unit(eighthsOfAPoint, UnitType.EighthOfAPoint);
    }

    public static Unit? HalfOfAPoint(double? halvesOfAPoint)
    {
      if (halvesOfAPoint == null)
        return null;

      return HalfOfAPoint(halvesOfAPoint.Value);
    }

    public static Unit HalfOfAPoint(double halvesOfAPoint)
    {
      return new Unit(halvesOfAPoint, UnitType.HalfOfAPoint);
    }

    public static Unit? Twip(double? twips)
    {
      if (twips == null)
        return null;

      return Twip(twips.Value);
    }

    public static Unit Twip(double twips)
    {
      return new Unit(twips, UnitType.Twip);
    }

    //public static Unit? Auto(double? autos)
    //{
    //  if (autos == null)
    //    return null;

    //  return Auto(autos.Value);
    //}

    //public static Unit Auto(double autos)
    //{
    //  return new Unit(autos, UnitType.Auto);
    //}

    public static Unit? Emu(double? emus)
    {
      if (emus == null)
        return null;

      return Emu(emus.Value);
    }

    public static Unit Emu(double emus)
    {
      return new Unit(emus, UnitType.Emu);
    }

    public static double? ToPixels(Unit? unit)
    {
      if (unit == null)
        return null;

      return unit.Value.ToPixels();
    }

    public bool Equals(Unit other)
    {
      return (_value == other._value && _type == other._type);
    }

    public override bool Equals(object obj)
    {
      if (obj is Unit)
        return Equals((Unit)obj);

      return false;
    }

    public override int GetHashCode()
    {
      return _value.GetHashCode() ^ _type.GetHashCode();
    }

    public override string ToString()
    {
      return "{" + _value + " " + _type + "}";
    }
  }
}